package com.vhall.component.service.watchlimit.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Splitter;
import com.vhall.component.dao.watchlimit.ApplyUsersMapper;
import com.vhall.component.dao.watchlimit.LimitApplyMapper;
import com.vhall.component.dao.watchlimit.WhiteAccountsMapper;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.watchlimit.ApplyEntity;
import com.vhall.component.entity.watchlimit.ApplyUserEntity;
import com.vhall.component.entity.watchlimit.WhiteAccountsEntity;
import com.vhall.component.entity.watchlimit.vo.*;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.constant.RoomConstant;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.common.utils.ReadExcel;
import com.vhall.component.service.room.RoomInnerService;
import com.vhall.component.service.watchlimit.WatchLimitConsoleService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author lsl
 * @since 2021/6/25
 */
@Service
public class WatchLimitConsoleServiceImpl implements WatchLimitConsoleService {

    private static final String LIMIT_TYPE = "limit_type";
    private static final String APPLY_ID = "apply_id";
    private static final String PAGE_ESCAPE = "\\/?page=";

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RoomInnerService roomInnerService;

    @Autowired
    private LimitApplyMapper limitApplyMapper;

    @Autowired
    private WhiteAccountsMapper whiteAccountsMapper;

    @Autowired
    private ApplyUsersMapper applyUsersMapper;

    private static final List<String> EXT_NAME_LIST = Arrays.asList("xls", "xlsx");

    /**
     * 用户名正则判断
     */
    private static final Pattern WHITE_NAME = Pattern.compile("^[\\w@.]{1,30}$");

    /**
     * 用户名正则判断
     */
    private static final Pattern WHITE_PAAS = Pattern.compile("^[A-Za-z0-9@_.]{1,30}$");

    @Override
    public JSONObject getWatchLimit(GetWatchLimitVO reqVO) {
        RoomsEntityDTO room = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (room == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        LambdaQueryWrapper<ApplyEntity> wrapper = Wrappers.lambdaQuery();
        // 为了兼容之前的il_id查询
        wrapper.eq(ApplyEntity::getRoomId, room.getRoomId());
        wrapper.isNull(ApplyEntity::getDeletedAt);
        wrapper.or();
        wrapper.eq(ApplyEntity::getIlId, room.getIlId());
        wrapper.isNull(ApplyEntity::getDeletedAt);
        wrapper.last(" limit 1 ");
        ApplyEntity apply = limitApplyMapper.selectOne(wrapper);
        JSONObject obj = new JSONObject();
        obj.put(LIMIT_TYPE, room.getLimitType());
        if (room.getLimitType() == 1) {
            if (apply == null) {
                obj.put(APPLY_ID, null);
            } else {
                obj.put(APPLY_ID, apply.getSourceId());
            }
        }
        return obj;
    }

    @Override
    public JSONObject setWatchLimit(SetWatchLimitVO reqVO) {
        RoomsEntityDTO room = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (room == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        if (reqVO.getLimitType() != null) {
            room.setLimitType(reqVO.getLimitType());
            room.setUpdatedAt(LocalDateTime.now());
            roomInnerService.updateByIl(room);
        }
        if (room.getLimitType() == RoomConstant.ACCOUNT_TYPE_APPROVE) {
            setVisitor(room.getIlId());
        }

        if (room.getLimitType() == RoomConstant.ACCOUNT_TYPE_APPEAR) {
            if (reqVO.getQuestionId() == null) {
                throw new BusinessException(BizErrorCode.BIZ_INVALID_PARAM);
            }
            LambdaQueryWrapper<ApplyEntity> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(ApplyEntity::getRoomId, reqVO.getRoomId());
            limitApplyMapper.delete(wrapper);
            ApplyEntity entity = createApply(reqVO, room.getIlId());
            limitApplyMapper.insert(entity);
            // 在这里删除缓存中存储的apply信息。
            redisTemplate.delete(RedisKey.APPLY_INFO_BY_ROOM_ID + room.getRoomId());
        }
        JSONObject obj = new JSONObject();
        obj.put(LIMIT_TYPE, reqVO.getLimitType());
        return obj;
    }

    @Override
    public WhiteUserListRespVO getWhitelist(WhiteListVO reqVO) {
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (roomInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        WhiteUserListRespVO resp = new WhiteUserListRespVO();
        int currPage = reqVO.getPage() == null ? 1 : reqVO.getPage();
        int pageSize = reqVO.getPageSize() == null ? 20 : reqVO.getPageSize();
        Page<WhiteAccountsEntity> page = new Page<>(currPage, pageSize);
        LambdaQueryWrapper<WhiteAccountsEntity> wrapper = Wrappers.lambdaQuery();
        // 为了兼容之前的il_id查询
        wrapper.eq(WhiteAccountsEntity::getRoomId, reqVO.getRoomId());
        wrapper.or();
        wrapper.eq(WhiteAccountsEntity::getIlId, roomInfo.getIlId());
        wrapper.orderByDesc(WhiteAccountsEntity::getCreatedAt);
        //差事件
        page = whiteAccountsMapper.selectPage(page, wrapper);

        List<WhiteUserVO> users = new ArrayList<>();
        page.getRecords().forEach(user -> users.add(convertToWhiteUserVO(user)));
        resp.setUsers(users);
        resp.setCurrentPage(currPage);
        resp.setLastPage((int) page.getPages());
        resp.setPerPage(pageSize);
        resp.setTotal((int) page.getTotal());
        resp.setFrom((currPage - 1) * pageSize + 1);
        resp.setTo(Math.min(currPage * pageSize, (int) page.getTotal()));
        int lastPage = (int) page.getPages();
        resp.setFirstPageUrl("\\/?page=1");
        resp.setLastPageUrl(PAGE_ESCAPE + (int) page.getPages());
        String prevPage = currPage == 1 ? null : PAGE_ESCAPE + (currPage - 1);
        String nextPage = currPage == lastPage ? null : PAGE_ESCAPE + (currPage + 1);
        resp.setPrevPageUrl(prevPage);
        resp.setNextPageUrl(nextPage);
        resp.setPath("\\/");
        return resp;
    }

    @Override
    public List<WhiteUserVO> searchWhitelist(SearchWhiteListVO reqVO) {
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (roomInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        LambdaQueryWrapper<WhiteAccountsEntity> wrapper = Wrappers.lambdaQuery();
        // 兼容之前的il_id查询
        wrapper.and(w -> w.eq(WhiteAccountsEntity::getRoomId, reqVO.getRoomId()).or()
                .eq(WhiteAccountsEntity::getIlId, roomInfo.getIlId()));
        wrapper.and(StringUtils.isNotEmpty(reqVO.getWhiteName()),
                w -> w.eq(WhiteAccountsEntity::getWhitename, reqVO.getWhiteName()));
        wrapper.orderByDesc(WhiteAccountsEntity::getCreatedAt);
        List<WhiteAccountsEntity> whites = whiteAccountsMapper.selectList(wrapper);
        List<WhiteUserVO> users = new ArrayList<>();
        whites.forEach(user -> users.add(convertToWhiteUserVO(user)));
        return users;
    }

    @Override
    public Integer delWhite(DelWhiteListVO reqVO) {
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (roomInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        List<Integer> ids = Splitter.on(",")
                .splitToList(reqVO.getWiIds())
                .stream()
                .filter(s -> (!StringUtils.isBlank(s)) && NumberUtil.isInteger(s))
                .map(s -> Integer.parseInt(s.trim()))
                .collect(Collectors.toList());
        if (ids.size() == 0) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_PARAM);
        }
        whiteAccountsMapper.deleteBatchIds(ids);
        return ids.size();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public JSONObject uploadWhitelist(UploadWhiteListVO reqVO, MultipartFile document) {
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (roomInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        if (document == null) {
            throw new BusinessException(BizErrorCode.TYPE_INVALID_UPLOAD);
        }
        String extName = FileUtil.extName(document.getOriginalFilename());
        if (!EXT_NAME_LIST.contains(extName)) {
            throw new BusinessException(BizErrorCode.TYPE_INVALID_UPLOAD);
        }
        LocalDateTime now = LocalDateTime.now();
        // 白名单累计导入数据不能超过50000条
        Integer countWhiteAccount = countWhiteAccount(reqVO.getRoomId(), roomInfo.getIlId());
        if (countWhiteAccount >= 50000) {
            throw new BusinessException(BizErrorCode.COMP_WATCHLIMIT_COUNT_OVERFLOW_TOTAL);
        }
        ReadExcel readExcel = new ReadExcel();
        // 解析excel，获取上传的事件单
        List<Map<String, Object>> excelList = readExcel.getExcelInfo(document);
        // 单次允许导入的最大条数
        Integer allowAccount = (50000 - countWhiteAccount) > 5000 ? 5000 : (50000 - countWhiteAccount);
        // 单次导入的条数
        Integer count = excelList.size();

        if (count > allowAccount) {
            throw new BusinessException(BizErrorCode.COMP_WATCHLIMIT_COUNT_OVERFLOW_SINGLE);
        }
        int errorTotalData = 0;
        Map<String, Object> map = new HashMap<>(16);
        for (Map<String, Object> data : excelList) {
            WhiteAccountsEntity white = new WhiteAccountsEntity();
            white.setWhitename(data.get("whitename").toString());
            if (Objects.isNull(data.get("whitepaas"))) {
                errorTotalData++;
                continue;
            }
            white.setWhitepaas(data.get("whitepaas").toString());
            if (!WHITE_NAME.matcher(white.getWhitename()).matches() || !WHITE_PAAS.matcher(white.getWhitepaas()).matches()) {
                errorTotalData++;
                continue;
            }
            white.setCreatedAt(now);
            white.setUpdatedAt(now);
            white.setIlId(roomInfo.getIlId());
            white.setRoomId(roomInfo.getRoomId());
            white.setLimitType(3);
            map.put(data.get("whitename").toString(), white);
        }

        // 去重之后
        List<WhiteAccountsEntity> correctList = map.values().stream()
                .map(s -> BeanUtil.toBean(s, WhiteAccountsEntity.class))
                .collect(Collectors.toList());
        // 正确的条数(去重之后)
        int correctTotalData = correctList.size();
        if (Objects.equals(correctTotalData, 0)) {
            if (errorTotalData > 0) {
                throw new BusinessException(BizErrorCode.COMP_WATCHLIMIT_ROW_INVALID);
            }
            throw new BusinessException(BizErrorCode.COMP_WATCHLIMIT_ROW_EMPTY);
        }
        // 重复
        List<Integer> updateIdList = new ArrayList<>();
        for (WhiteAccountsEntity white : correctList) {
            LambdaQueryWrapper<WhiteAccountsEntity> where = Wrappers.lambdaQuery();
            where.eq(WhiteAccountsEntity::getWhitename, white.getWhitename());
            // 兼容之前的il_id查询
            where.and(wq -> wq.eq(WhiteAccountsEntity::getRoomId, roomInfo.getRoomId()).or().eq(WhiteAccountsEntity::getIlId, roomInfo.getIlId()));
            where.last("limit 1");
            WhiteAccountsEntity entity = whiteAccountsMapper.selectOne(where);
            if (entity == null) {
                continue;
            }
            Integer id = entity.getId();
            updateIdList.add(id);
        }
        if (updateIdList.size() != 0) {
            whiteAccountsMapper.deleteBatchIds(updateIdList);
        }
        whiteAccountsMapper.batchInsert(correctList);

        JSONObject obj = new JSONObject();
        obj.put("correctdata", correctTotalData);
        obj.put("errordata", errorTotalData);
        obj.put("repeat", updateIdList.size());
        return obj;
    }

    private Integer countWhiteAccount(String roomId, Integer ilId) {
        LambdaQueryWrapper<WhiteAccountsEntity> where = Wrappers.lambdaQuery();
        where.eq(WhiteAccountsEntity::getRoomId, roomId);
        where.or();
        where.eq(WhiteAccountsEntity::getIlId, ilId);
        return whiteAccountsMapper.selectCount(where);
    }

    @Override
    public JSONObject apply(ReplyVO reqVO) {
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (roomInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        ApplyEntity apply = getLimitApply(reqVO.getRoomId(), roomInfo.getIlId());
        if (apply == null) {
            throw new BusinessException(BizErrorCode.EMPTY_SIGN_TABLE);
        }
        if (apply.getSourceId() == null || !Objects.equals(apply.getSourceId(), reqVO.getQuestionId())) {
            throw new BusinessException(BizErrorCode.EMPTY_SIGN_TABLE);
        }
        ApplyUserEntity applyUser = applyUsersMapper.findApplyUser(reqVO.getRoomId(), roomInfo.getIlId(), reqVO.getPhone());
        if (applyUser == null) {
            applyUser = new ApplyUserEntity();
            applyUser.setIlId(roomInfo.getIlId());
            applyUser.setRoomId(roomInfo.getRoomId());
            applyUser.setAnswerId(String.valueOf(reqVO.getAnswerId()));
            applyUser.setPhone(reqVO.getPhone());
            applyUser.setLimitType(1);
            applyUser.setApplyId(reqVO.getQuestionId());
            LocalDateTime now = LocalDateTime.now();
            applyUser.setCreatedAt(now);
            applyUser.setUpdatedAt(now);
            applyUsersMapper.insert(applyUser);
        } else {
            applyUser.setAnswerId(String.valueOf(reqVO.getAnswerId()));
            applyUser.setApplyId(reqVO.getQuestionId());
            applyUser.setUpdatedAt(LocalDateTime.now());
            applyUsersMapper.updateById(applyUser);
        }
        JSONObject obj = new JSONObject();
        JSONObject applyObj = new JSONObject();
        applyObj.put("phone", reqVO.getPhone());
        applyObj.put("answer_id", reqVO.getAnswerId() + "");
        applyObj.put(APPLY_ID, reqVO.getQuestionId() + "");
        applyObj.put("il_id", roomInfo.getIlId() + "");
        applyObj.put(LIMIT_TYPE, 1);
        obj.put(LIMIT_TYPE, applyObj);
        return obj;
    }


    /**
     * 获取报名信息
     *
     * @param roomId
     * @return
     */
    private ApplyEntity getLimitApply(String roomId, Integer ilId) {
        ApplyEntity entity = null;
        Object cast = redisTemplate.opsForValue().get(RedisKey.APPLY_INFO_BY_ROOM_ID + roomId);
        if (cast != null) {
            entity = JSON.parseObject(cast.toString(), ApplyEntity.class);
        } else {
            // 为了兼容之前的il_id查询
            LambdaQueryWrapper<ApplyEntity> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(ApplyEntity::getRoomId, roomId);
            wrapper.or();
            wrapper.eq(ApplyEntity::getIlId, ilId);
            wrapper.last(" limit 1 ");
            entity = limitApplyMapper.selectOne(wrapper);
        }
        if (entity != null) {
            redisTemplate.opsForValue().set(RedisKey.APPLY_INFO_BY_ROOM_ID + roomId, JsonUtil.toJsonString(entity));
        }
        return entity;
    }

    /**
     * 针对游客设置  也就是默认模式
     *
     * @param ilId
     */
    private void setVisitor(Integer ilId) {
        //响亮说 没有用
    }

    /**
     * 构造apply对象
     *
     * @param reqVO
     * @return
     */
    private ApplyEntity createApply(SetWatchLimitVO reqVO, Integer ilId) {
        ApplyEntity entity = new ApplyEntity();
        entity.setIlId(ilId);
        entity.setRoomId(reqVO.getRoomId());
        entity.setSourceId(reqVO.getQuestionId());
        entity.setLimitType(reqVO.getLimitType());
        entity.setCreatedAt(LocalDateTime.now());
        entity.setUpdatedAt(entity.getCreatedAt());
        return entity;
    }

    private WhiteUserVO convertToWhiteUserVO(WhiteAccountsEntity user) {
        WhiteUserVO vo = new WhiteUserVO();
        vo.setDesc(user.getDesc());
        vo.setId(user.getId());
        vo.setWhiteName(user.getWhitename());
        vo.setWhitePaas(user.getWhitepaas());
        vo.setIlId(user.getIlId());
        return vo;
    }
}
